home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Workbench Add-On
/
Workbench Add-On - Volume 1.iso
/
Music
/
PLAY
/
MultiPlayer
/
MultiPlayer132Src.lha
/
player.asm
< prev
next >
Wrap
Assembly Source File
|
1992-09-14
|
65KB
|
2,301 lines
* MultiPlayer
* Copyright (C) 1992 Bryan Ford
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* I (the author of MultiPlayer) can be contacted on the Internet at
* "bryan.ford@m.cc.utah.edu". See "Player.doc" for other addresses.
*
* $Id: player.asm,v 5.1 92/09/14 18:43:30 BAF Exp $
*
* System includes
include "exec/types.i"
include "exec/nodes.i"
include "exec/memory.i"
include "exec/interrupts.i"
include "exec/funcdef.i"
include "exec/exec_lib.i"
include "exec/execbase.i"
include "exec/ables.i"
include "hardware/cia.i"
include "hardware/intbits.i"
include "hardware/dmabits.i"
include "hardware/custom.i"
include "resources/cia.i"
include "dos/dos.i"
include "dos/dos_lib.i"
include "intuition/intuition_lib.i"
include "libraries/xpk.i"
include "bry/macros.i"
_LVOAddICRVector equ -$06 ; LVO offsets for ciab.resource
_LVORemICRVector equ -$0c
_LVOAbleICR equ -$12
_LVOSetICR equ -$18
include "player.i"
code text
xref toupper,toupperlong,stripblanks,isalpha
xref allocaudio,freeaudio,intsongpos,intrepeat,clearflash
xref _progload,_progstart
xref NoteSys,NoteSysMasterVolBal
xref ntstart,trekstart,ntpackstart,ststart,npstart
xref fcostart,fc14start,dssstart
xref jamstart,soundmonstart
xref oktastart,medstart
xref soundstart
xref _LinkerDB,_SysBase,_DOSBase,_IntuitionBase,_BattMemBase,_XpkBase
xref _sysflags,_playmode,_flashflags
xref GlobSetByte,GlobSetWord,GlobSetLong
xref mvolume,mbalance,mspeed,volume,balance,speed,modflags,filter
xref _infowinattach,_infowindetach
xdef loadfile,unloadfile
xdef loadmod,startmod,playmod,stopmod,endmod,contmod,contplaymod,setsong
xdef _loadmod,_startmod,_playmod,_stopmod,_endmod,_contmod,_contplaymod,_setsong,_iscantcont
xdef dienomem,diecorrupt,dieerrplay,diemes
xdef settint,setnormtint,stoptint,hookfunc,dmawait
xdef getfreqmodspeed,ntgetsongname,ntgetsongauthor,parseauthor,prepstring,addinfo
xdef changetint,changetintcia
xdef playervolume,playerspeed,_playervolume,_playerspeed
xdef addinfo,addinfopad
xdef allocmod,allocmodchip
xdef modtype,numsongs,cursong
xdef modspec,modmem,modsize,modend
xdef suppmem,suppsize,suppend
xdef modname,authorname,modtypename,songcatalog
xdef jumpforward,jumpback
xdef _modtype,_modname,_authorname,_modtypename,_songcatalog,_playing,_infolist
xdef _numsongs,_cursong,_songtime,_songendtime,_songpos,_songlen
xdef _fadevol,_fadeinc
xdef nocontinue ; For rexxcont's benefit
xdef nomemtext
*** gmodcheck - Check to see if a particular entry in the GMOD header is used
* d0 = Offset of entrypoint
* a5 = GMOD header
* Returns: NZ if entrypoint is used, Z if it is nonexistent or do-nothing
gmodcheck:
gmodbout d0,9$
cmp.w #RTSINST,0(a5,d0.l)
rts
9$ cmp.l d0,d0
rts
*** closeloadhan - Close any currently opened loadhan
closeloadhan:
move.l loadhan(a4),d1 ; Close the loading handle if opened
beq.s 1$
move.l a6,-(sp)
move.l _DOSBase(a4),a6
jsr _LVOClose(a6)
clr.l loadhan(a4)
move.l (sp)+,a6
1$ rts
*** allocmod/allocmodchip - Allocate memory attached to this module (freed when module is unloaded)
* d0 = Amount of memory to allocate
* Returns: d0 = Memory block, NULL on failure
allocmodchip
move.l #MEMF_CHIP,d0
bra allocmod_in
allocmod
moveq #0,d1
allocmod_in
move.l a6,-(sp)
move.l _IntuitionBase(a4),a6
jsr _LVOAllocRemember(a6)
move.l (sp)+,a6
rts
*** loadfile - Load a file into a memory filespec
* a0 = Pointer to filename
* a1 = Pointer to filespec
* Returns: NE if successful, EQ if file not found, die if other error
loadfile:
apush
move.l a1,a2
tst.l fs_Mem(a2) ; See if we can decompress
bne.s 1$ ; Can't decompress on a reload
move.l _XpkBase(a4),d0
bne 5$
1$
move.l a0,d1 ; Open module
move.l #MODE_OLDFILE,d2
move.l _DOSBase(a4),a6
jsr _LVOOpen(a6)
move.l d0,loadhan(a4)
move.l d0,d7
beq 99$
move.l d7,d1 ; Seek to end of module
moveq #0,d2
move.l #OFFSET_END,d3
jsr _LVOSeek(a6)
tst.l d0
bne dienofile
move.l d7,d1 ; Seek back to beginning and find size
moveq #0,d2
move.l #OFFSET_BEGINNING,d3
jsr _LVOSeek(a6)
move.l d0,d6
ble dienofile
move.l fs_Mem(a2),d0 ; See if the memory is already allocated
beq.s 2$
cmp.l fs_FileSize(a2),d6 ; Make sure the file wasn't compressed
beq.s 3$
bra diedontcompress
2$
move.l _SysBase(a4),a6 ; Allocate memory
move.l d6,d0
moveq #MEMF_CHIP!MEMF_PUBLIC,d1
jsr _LVOAllocMem(a6)
move.l d0,fs_Mem(a2)
beq dienocmem
move.l d6,fs_FileSize(a2)
move.l d6,fs_AllocSize(a2)
3$ move.l d0,d2
add.l d6,d0
move.l d0,fs_EndMem(a2)
move.l _DOSBase(a4),a6 ; Load module
move.l d7,d1
move.l d6,d3
jsr _LVORead(a6)
cmp.l d0,d6
bne dienofile
bsr closeloadhan ; Close module
bra 9$
5$ move.l d0,a6
clr.l -(sp) ; Set up the TagList
lea xpkerrmsg(a4),a1
move.l a1,-(sp)
move.l #XPK_GetError,-(sp)
moveq #1,d1
move.l d1,-(sp)
move.l #XPK_PassThru,-(sp)
moveq #MEMF_CHIP,d1
move.l d1,-(sp)
move.l #XPK_OutMemType,-(sp)
lea fs_FileSize(a2),a1
move.l a1,-(sp)
move.l #XPK_GetOutLen,-(sp)
lea fs_AllocSize(a2),a1
move.l a1,-(sp)
move.l #XPK_GetOutBufLen,-(sp)
move.l a2,-(sp)
move.l #XPK_GetOutBuf,-(sp)
move.l a0,-(sp)
move.l #XPK_InName,-(sp)
move.l sp,a0 ; Try to load the module
jsr _LVOXpkUnpack(a6)
adda.w #7*8+4,sp ; Pop the TagList off the stack
tst.l d0
bne.s 88$
move.l fs_Mem(a2),d0 ; Calculate the module end position
add.l fs_FileSize(a2),d0
move.l d0,fs_EndMem(a2)
9$ moveq #1,d0
99$ apop
rts
88$ moveq #XPKERR_IOERRIN,d1 ; See if it was a DOS error
cmp.l d0,d1
beq 99$
moveq #XPKERR_NOMEM,d1 ; See if it was not enough memory
cmp.l d0,d1
beq dienocmem
lea xpkerrmsg(a4),a0 ; Use XPK's error message
bra diemes
*** unloadfile - Unload a file (if loaded)
* a0 = Pointer to filespec
unloadfile:
move.l fs_Mem(a0),a1
clr.l fs_Mem(a0)
move.l a1,d0
beq.s 9$
move.l fs_AllocSize(a0),d0
move.l a6,-(sp)
move.l _SysBase(a4),a6
jsr _LVOFreeMem(a6)
move.l (sp)+,a6
9$ rts
*** findauthor - Find the author of the current song (into authorname)
* a5 = Pointer to GMOD header
findauthor:
moveq #0,d0
move.l cursong(a4),d1
movea.w #gmod_GetSongAuthor,a1
gmodmaycall a1
move.l d0,authorname(a4)
bne.s 9$
lea unknowntxt(a4),a0
move.l a0,authorname(a4)
9$ rts
*** findcursong - Find the song to start playing, adjusting if necessary
* Returns: d0 = Adjusted song number
findcursong:
move.l cursong(a4),d0 ; Check the "recommended" song number
bpl.s 19$ ; See if it's the user's option
cmp.b #PM_RANDOM,_playmode(a4)
bne.s 15$
moveq #0,d0 ; FIXME: Select random song
bra.s 19$
15$ moveq #0,d0
19$
cmp.l numsongs(a4),d0 ; Make sure it's under the maximum
bcs.s 49$
moveq #0,d0 ; Always wrap around to song 0
49$
move.l d0,cursong(a4) ; Store it back into cursong
rts
*** setsong - Start playing a different song in the current module
* d0 = Song number to start playing
_setsong
move.l 4(sp),d0
setsong
move.l numsongs(a4),d1 ; Make sure there are at least two songs
subq.l #1,d1
beq.s 99$
move.l d0,cursong(a4) ; Save new song number
movem.l a5-a6,-(sp)
move.l gmodptr(a4),a5
move.l _SysBase(a4),a6
* DISABLE ; No interrupts while song is stopped
moveq #gmod_StopMusic,d0 ; Stop the current song
gmodmaycall d0
bsr findcursong ; Find/adjust the song to play
moveq #gmod_StartMusic,d1 ; Start the new music
gmodmaycall d1
clr.l _songtime(a4) ; Clear the song timer
9$ bsr findauthor ; See who wrote this song
* ENABLE
movem.l (sp)+,a5-a6
99$ bset #SB_WINDOWUP,_sysflags(a4)
rts
*** catalogsongs - Create a catalog of songs for the current module
* a5 = GMOD pointer
catalogsongs:
apush4
lea modremember(a4),a0 ; Allocate memory for catalog
move.l numsongs(a4),d0
move.l d0,d2
addq.l #1,d0
lsl.l #2,d0
bsr allocmod
move.l d0,songcatalog(a4)
beq dienomem
move.l d0,a2 ; Create the song list
movea.w #gmod_GetSongName,a6
moveq #0,d3
11$ move.l d3,d1
moveq #0,d0
gmodmaycall a6
move.l d0,(a2)+
bne.s 15$
lea unknowntxt(a4),a0
move.l a0,-4(a2)
15$ addq.l #1,d3
subq.l #1,d2
bne.s 11$
clr.l (a2)
apop4
rts
*** freemodfile - Free the modfile string
freemodfile:
move.l modfile(a4),d0
beq.s 9$
move.l a6,-(sp)
move.l d0,a1
move.l modfilesize(a4),d0
move.l _SysBase(a4),a6
jsr _LVOFreeMem(a6)
clr.l modfile(a4)
move.l (sp)+,a6
9$ rts
*** setmodfile - Copy a filename into the modfile buffer
* a0 = Pointer to filename to copy
setmodfile:
movem.l a2/a6,-(sp)
move.l a0,a2
bsr freemodfile
move.l a2,a0 ; Find the string length
moveq #0,d0
1$ addq.l #1,d0
tst.b (a0)+
bne.s 1$
move.l d0,modfilesize(a4)
moveq #0,d1 ; Allocate the modfile buffer
move.l _SysBase(a4),a6
jsr _LVOAllocMem(a6)
move.l d0,modfile(a4)
beq dienomem
move.l d0,a0 ; Copy the buffer
2$ move.b (a2)+,(a0)+
bne.s 2$
movem.l (sp)+,a2/a6
rts
*** addinfopad - Add a pad line to the info list (only if the list is already started)
addinfopad
tst.l infolist(a4)
bz addinfo_rts
lea nulllab(a4),a0
; Fall through...
*** addinfo - Add a string to the info list (allocate a new info list if necessary)
* a0 = Text
addinfo
apush4
move.l a0,a3
move.l _IntuitionBase(a4),a6
bsr _infowindetach
tst.l infolist(a4) ; Make sure we have a list to put things on
bnz.b \gotlist
lea modremember(a4),a0
moveq #MLH_SIZE,d0
moveq #0,d1
jl AllocRemember
move.l d0,infolist(a4)
bz \out
move.l d0,a1
NEWLIST a1
\gotlist
\lloop
move.l a3,a2
moveq #-1,d2
\loop
addq.w #1,d2 ; Search for a null in this line
move.b (a3)+,d0
bz.b \add
cmp.b #10,d0
beq.b \add
cmp.w #INFOCOLS,d2
blo.b \loop
move.l a3,a1 ; Search backwards for a word break
move.w d2,d1
\bloop
subq.w #1,d2
cmp.w #INFOCOLS/2,d2 ; Fill at least half a line
beq.b \lbreak
cmp.b #' ',-(a3)
bne.b \bloop
addq.w #1,d2
addq #1,a3
bra.b \add
\lbreak
lea -1(a1),a3 ; Break without regard to words
move.w d1,d2
\add
lea modremember(a4),a0 ; Allocate a new info node
moveq #LN_SIZE+1,d0
add.w d2,d0
moveq #0,d1
jl AllocRemember
tst.l d0
bz.b \out
move.l d0,a1 ; Add it to the list
move.l infolist(a4),a0
ADDTAIL
lea LN_SIZE(a1),a1 ; Copy the string into it
move.l a1,LN_NAME-LN_SIZE(a1)
bra.b \copyin
\copy
move.b (a2)+,(a1)+
\copyin
dbra d2,\copy
clr.b (a1)
tst.b -1(a3) ; Next line
bnz.b \lloop
\out
bsr _infowinattach
apop4
addinfo_rts
rts
*** ldmod - (Internal) - Load module used by loadmod and startmod
* a0 = Pointer to filename
* Returns: CCR, d0 = progload()+1, 0 if everything successful, dies on failure
ldmod:
move.l modfile(a4),a0 ; Find the filename to display
move.l (a0),d0
bsr toupperlong
cmpi.l #'MOD.',d0
bne.s 19$
addq.l #4,a0
19$ move.l a0,modname(a4)
lea modspec(a4),a1 ; Load the module
move.l modfile(a4),a0
bsr loadfile
beq dienofile
move.l modmem(a4),a0 ; See if this is a program
cmpi.l #'PROG',(a0)
beq \isprog
; FIXME: Load mod prefs
move.l modmem(a4),a0
pea 5$
move.l (a0),d0 ; Modules IDable in first long
cmpi.l #'GMOD',d0 ; General executable module
beq gmodstart
cmpi.l #'XMOD',d0 ; Executable module
beq xmodstart
cmpi.l #'AMOD',d0 ; Absolute executable module
beq amodstart
cmpi.l #'BeEp',d0 ; JamCracker module
beq jamstart
cmpi.l #'SMOD',d0 ; Future Composer old module
beq fcostart
cmpi.l #'FC14',d0 ; Future Composer 1.4 module
beq fc14start
cmpi.l #'OKTA',d0 ; Oktalyzer module
beq oktastart
cmpi.l #'MMD0',d0 ; MED 3.xx module
beq medstart
cmpi.l #'MMD1',d0 ; OctaMED Pro module
beq medstart
cmpi.l #'MMU2',d0 ; Digital Sound Studio module
beq dssstart
cmpi.l #$48e7f1fe,d0 ; Dave Whittaker module
beq whitstart
cmpi.l #$08f90001,d0 ; SidMon 2.1 module
beq sid2start
cmpi.l #$48e700f0,d0 ; Mark II module
beq markiistart
clr.b d0 ; Face The Music module
cmpi.l #'FTM'<<8,d0
beq ftmstart
cmpi.l #'8SVX',$8(a0) ; 8SVX sounds (possibly sequenced)
beq soundstart
move.l $1a(a0),d0 ; SoundMonitor module
clr.b d0
cmpi.l #'V.2'<<8,d0
beq soundmonstart
cmpi.l #$4eaefeda,$80(a0) ; SoundFX executable module
beq fxstart
cmpi.l #'PATT',$1f6(a0) ; NoiseTracker packed module
beq ntpackstart
move.l $438(a0),d0 ; NoiseTrackerish module
cmpi.l #'M.K.',d0
beq ntststart
cmpi.l #'FLT4',d0
beq ntststart
cmpi.l #'EXO4',d0
beq ntststart
cmpi.l #'MTN'<<8,$5b8(a0) ; SoundTracker 2.0-2.6 module
beq ststart
cmpi.l #'TA1A',$51c(a0) ; Delta Music module
beq deltastart
cmpi.l #'TA1A',$27c(a0) ; Delta Music module
beq delta2start
cmpi.w #$4efa,(a0) ; Generic embedded module
beq genembstart
move.l a0,a1 ; NoisePacker module
move.w (a1),d0
cmp.w #$1c,d0
blo \notnp
lea -4(a1,d0.w),a2
tst.l (a1)+
bz \notnp
tst.l (a1)+
bz \notnp
\nptest
cmp.l a2,a1
bhs npstart
tst.l (a1)+
bnz \notnp
tst.l (a1)+
bnz \nptest
\notnp
moveq #$60,d1 ; ST/NT module
move.b (a0),d0 ; FIXME: Need better detection here
and.b d1,d0
beq.s 51$
move.b $13(a0),d0
and.b d1,d0
beq ntstart
51$ bra dieunkmod ; Doesn't match anything we know of
5$ move.l d0,gmodptr(a4) ; Save the GMOD header pointer
move.l d0,a5
moveq #gmod_NotePlayer,d1 ; Give the module our NotePlayer
moveq #0,d0
lea NoteSys,a0
gmodmaycall d1
tst.l d0
sne portablemodule(a4)
moveq #gmod_InitMusic,d1 ; Call the initialization vector
moveq #0,d0
gmodmaycall d1
tst.l d0
beq.s 39$
move.l d0,a0
bra diemes
39$
moveq #gmod_GetNumSongs,d1 ; Find the number of songs
moveq #0,d0
gmodmaycall d1
tst.l d0
bne.s 7$
moveq #1,d0
7$ move.l d0,numsongs(a4)
moveq #gmod_GetMakerName,d1 ; Find the module type
moveq #0,d0
gmodmaycall d1
move.l d0,modtypename(a4)
bne.s 61$
lea gmodname(a4),a0
move.l a0,modtypename(a4)
61$
bsr catalogsongs ; Catalog the songs in the module
bsr findauthor ; See who wrote this song
moveq #0,d0 ; Put the scrolltext into the info window
moveq #gmod_GetScroll,d1
gmodmaycall d1
tst.l d0
bz.b \noscroll
move.l d0,-(sp)
bsr addinfopad
move.l (sp)+,a0
bsr addinfo
\noscroll
moveq #gmod_Hook,d7 ; Give the module our Hook
lea playerhook(a4),a0
move.l #GMODHF_REPEAT!GMODHF_SEQUENCE,d1
moveq #0,d0
gmodmaycall d7
btst #GMODHB_SEQUENCE,d0
bz.s \nohookseq
bset #MTB_SEQUENCE,modtype(a4)
\nohookseq:
btst #GMODHB_REPEAT,d0
bz.s \nohookrepeat
bset #MTB_REPEAT,modtype(a4)
\nohookrepeat:
btst.b #MTB_FILTER,modtype(a4) ; See if we can control the filter
bnz.b \hasfilt
lea filter(a4),a0
sub.l a1,a1
moveq #-1,d0
bsr GlobSetByte
\hasfilt
btst.b #MTB_PROTRACKER,modtype(a4) ; See if it's the Protracker player
bnz.b \ispt
lea modflags(a4),a0
move.b (a0),d0
bset.b #MNB_NOTPROTRACKER,d0
bnz.b \ispt
sub.l a1,a1
bsr GlobSetByte
\ispt
btst #MTB_SEQUENCE,modtype(a4) ; See if the player can jump around
bz.b \nojump
moveq #gmod_Jump,d0
bsr gmodcheck
bz.b \nojump
bset.b #MTB_JUMP,modtype(a4)
\nojump
moveq #0,d0
rts
\isprog: ; We found a module program!
clr.l -(sp) ; Load the program
move.l modfile(a4),-(sp)
clr.l -(sp)
bsr _progload
lea 12(sp),sp
move.l d0,d2
bsr endmod ; Unload what we loaded
move.l d2,d0
addq.l #1,d0
rts
*** plmod - (Internal) - Play module used by playmod and startmod
* d0 = 0 to start, 1 to continue
* Returns 0 if successful, 1 if couldn't continue, dies on other errors
plmod:
move.l d0,d7 ; Save start/continue flag
tst.b _playing(a4) ; Don't restart if already playing
bne 9$
move.l gmodptr(a4),d0 ; Make sure a module is loaded
beq dienotloaded
move.l d0,a5
move.b noaudalloc(a4),d0
or.b portablemodule(a4),d0
bnz \noalloc
bsr allocaudio ; Allocate the audio.device
\noalloc
tst.l d7 ; Start or continue?
bne.s 15$
move.l _SysBase(a4),a1 ; Find the clock frequency
move.l #715909,d0
cmp.b #50,PowerSupplyFrequency(a1)
bne.s \ntsc
move.l #709379,d0
\ntsc move.l d0,clockfreq(a4)
bsr findcursong ; Find the song to start playing
moveq #gmod_StartMusic,d1 ; Start playing the music
move.l modmem(a4),a0 ; Convenience for built-in players
gmodmaycall d1
clr.l _songtime(a4) ; Clear the song timer
bclr.b #SB_REPEAT,_sysflags(a4) ; Kill any stray repeat flags
bra.s 19$
15$
moveq #gmod_ContinueMusic,d1 ; Try to continue the music
moveq #0,d0
gmodmaycall d1
tst.l d0
beq 7$
19$
tst.b portablemodule(a4) ; Set the module's master volume
bnz \hasvol
moveq #gmod_SetVolume,d0
bsr gmodcheck
bz.b \novol
\hasvol
st.b _playing(a4)
bsr playervolume
clr.b _playing(a4)
bra.b \finvol
\novol
lea volume(a4),a0 ; Disable the volume control
sub.l a1,a1
moveq #-1,d0
bsr GlobSetWord
\finvol
lea audisave(a4),a3 ; Set the audio channel interrupts
lea audints(a4),a2
moveq #gmod_Audio0,d2
moveq #INTB_AUD0,d3
move.w #INTF_SETCLR,d4
bsr 88$
bsr 88$
bsr 88$
bsr 88$
move.w d4,CUSTOM+intena
moveq #gmod_VBlank50,d0 ; Set the timing interrupt
bsr gmodcheck
beq.s 21$
moveq #50,d0
lea gmod_VBlank50(a5),a0
bra.s 28$
21$ moveq #gmod_VBlank60,d0
bsr gmodcheck
beq.s 22$
moveq #60,d0
lea gmod_VBlank60(a5),a0
bra.s 28$
22$ moveq #gmod_TimerTick,d0
bsr gmodcheck
beq.s 29$
moveq #0,d0
jsr gmod_GetFrequency(a5)
tst.l d0
beq.s 29$
bpl.s 27$ ; Convenience for internal players
moveq #50,d0
bset #MTB_MODSPEED,modtype(a4)
27$ lea gmod_TimerTick(a5),a0
28$ move.l modmem(a4),a1 ; a1 = module pointer (for our players)
bsr settint
bra.b \speeddone
29$
lea speed(a4),a0 ; No speed control - disable the speed gadget
sub.l a1,a1
moveq #0,d0
bsr GlobSetWord
\speeddone
cmp.b #2,filter(a4) ; Set the filter
bne.b \filtoff
\filton
bclr.b #CIAB_LED,CIAA+ciapra
bra.b \filtdone
\filtoff
bset.b #CIAB_LED,CIAA+ciapra
\filtdone
st.b _playing(a4) ; Set playing flag
9$ moveq #0,d0 ; Return success code
rts
88$ move.l a2,a1
move.l d2,d0
bsr gmodcheck
beq.s 89$
move.b #NT_INTERRUPT,LN_TYPE(a1) ; Set the interrupt vector
lea audiointname(a4),a0
move.l a0,LN_NAME(a1)
lea 0(a5,d2.w),a0
move.l a0,IS_CODE(a1)
move.l a4,IS_DATA(a1)
move.l d3,d0
move.l _SysBase(a4),a6
jsr _LVOSetIntVector(a6)
move.l d0,(a3)
bset d3,d4 ; Turn on the interrupt later
89$ addq.l #4,d2
addq.l #4,a3
addq.l #1,d3
adda.w #IS_SIZE,a2
rts
7$ bsr stpmod ; Stop whatever was started
moveq #1,d0 ; Return can't continue error code
rts
*** stpmod - (Internal) - Stop a playing module
stpmod:
tst.b _playing(a4) ; Turn the LED back on
bz.b \noled
bclr.b #CIAB_LED,CIAA+ciapra
\noled
clr.b _playing(a4) ; Clear the playing flag
bsr stoptint ; Stop timer interrupts
lea audisave(a4),a2 ; Stop audio channel interrupts
moveq #INTB_AUD0,d2
move.w #INTF_AUD0!INTF_AUD1!INTF_AUD2!INTF_AUD3,CUSTOM+intena
move.w #INTF_AUD0!INTF_AUD1!INTF_AUD2!INTF_AUD3,CUSTOM+intreq
bsr.s 88$
bsr.s 88$
bsr.s 88$
bsr.s 88$
bra.s 85$
88$ move.l d2,d0
addq.l #1,d2
move.l (a2)+,d1
beq.s 89$
clr.l -4(a2)
move.l d1,a1
move.l _SysBase(a4),a6
jmp _LVOSetIntVector(a6)
89$ rts
85$
move.l gmodptr(a4),d0 ; Stop playing the music
beq.s 29$
move.l d0,a5
moveq #gmod_StopMusic,d0
gmodmaycall d0
29$
bsr clearflash
bra freeaudio ; Free the audio channels
*** loadmod - Load a module without playing it
* a0 = Pointer to filename
* Returns: d0 = Null if successful, pointer to error message if failed
_loadmod
move.l 4(sp),a0
loadmod
movem.l d2-d7/a2-a6,-(sp)
move.l a0,a2
bsr endmod ; First make sure nothing is playing
move.l sp,stackpt(a4) ; Save the stack pointer in case we die
move.l a2,a0 ; Set/allocate the modfile string
bsr setmodfile
bsr ldmod ; Load the module
bz.s 9$
subq.l #1,d0 ; Find the real return code
9$ bset #SB_WINDOWUP,_sysflags(a4)
movem.l (sp)+,d2-d7/a2-a6
rts
*** playmod - Play an already-loaded module
* d0 = Song number (-1 = user's choice)
* Returns: d0 = Null if successful, pointer to error message if failed
_playmod
move.l 4(sp),d0
playmod
movem.l d2-d7/a2-a6,-(sp)
tst.l d0 ; Only change songs if it's specified
bmi.s 1$
move.l d0,cursong(a4) ; Save the song number
1$
bsr stpmod ; Stop any previously playing song
move.l sp,stackpt(a4) ; Save the stack pointer in case we die
moveq #0,d0 ; Start playing the module
bsr plmod
bset #SB_WINDOWUP,_sysflags(a4)
moveq #0,d0 ; Return a success code
movem.l (sp)+,d2-d7/a2-a6
rts
*** startmod - Load a module and start it playing
* a0 = Pointer to filename
* d0 = Song number (-1 = user's choice)
* Returns: d0 = Null if successful, pointer to error message if failed
_startmod
move.l 4(sp),a0
move.l 8(sp),d0
startmod
movem.l d2-d7/a2-a6,-(sp)
move.l d0,cursong(a4) ; Save the song number
move.l a0,a2
bsr endmod ; First make sure nothing is playing
move.l sp,stackpt(a4) ; Save the stack pointer in case we die
move.l a2,a0 ; Set/allocate the modfile string
bsr setmodfile
bsr ldmod ; Load the module
bz.s 5$
subq.l #1,d0
bnz.s 9$
move.l d0,-(sp)
move.l a0,-(sp)
bsr _progstart ; Start the program too
addq #8,sp
bra.s 9$
5$ moveq #0,d0 ; Play the module
bsr plmod
moveq #0,d0
9$ bset #SB_WINDOWUP,_sysflags(a4)
movem.l (sp)+,d2-d7/a2-a6
rts
*** contmod - Continue a module from where it left off
* Returns: d0 = Null if successful, error message if failed
contmod
_contmod
tst.b _playing(a4) ; See if it's already playing
bne.s 99$
movem.l d2-d7/a2-a6,-(sp)
move.l sp,stackpt(a4) ; Save the stack pointer in case we die
moveq #1,d0 ; Continue the module
bsr plmod
beq.s 19$
lea nocontinue(a4),a0 ; Can't continue - error message
move.l a0,d0
19$
movem.l (sp)+,d2-d7/a2-a6
rts
bset #SB_WINDOWUP,_sysflags(a4)
99$ moveq #0,d0 ; Return a success code immediately
rts
*** contplaymod - Continue current module if possible, otherwise restart it
* Returns: d0 = Null if successful, error message if failed
contplaymod:
_contplaymod:
bsr contmod
lea nocontinue(a4),a0
cmp.l a0,d0
bne.s 9$
moveq #-1,d0
bsr playmod
9$ rts
*** stopmod - Stop a playing module
* Returns: d0 = NULL
stopmod:
_stopmod:
movem.l d2-d7/a2-a6,-(sp) ; Stop the module
bsr stpmod
movem.l (sp)+,d2-d7/a2-a6
bset #SB_WINDOWUP,_sysflags(a4)
moveq #0,d0
rts
*** endmod - Stop and unload a module
* Returns: d0 = NULL
endmod:
_endmod:
movem.l d2-d7/a2-a6,-(sp)
move.l sp,stackpt(a4)
moveq #0,d3
bra.s die
*** die - and other forms of death (from the module player only)
dienotloaded: ; No module loaded
lea notloaded(a4),a0
bra.s diemes
diedontcompress: ; Reloaded compressed module
lea dontcompress(a4),a0
bra.s diemes
dienofile: ; File not found
lea nofile(a4),a0
bra.s diemes
dieunkmod: ; File is not a module
lea unkmod(a4),a0
bra.s diemes
diecorrupt: ; Module is corrupt
lea cormod(a4),a0
bra.s diemes
dieerrplay: ; Error playing module
lea errplay(a4),a0
bra.s diemes
dienoabs:
lea noabs(a4),a0 ; Couldn't allocate absolute memory
bra.s diemes
dienotimer:
lea notimer(a4),a0 ; Couldn't allocate either CIAB timer
bra.s diemes
dienocmem: ; Not enough memory
lea nocmem(a4),a0
bra.s diemes
dienomem: ; Not enough memory
lea nomemtext(a4),a0
diemes:
move.l a0,d3
die:
bsr stpmod ; Stop playing the module
move.l gmodptr(a4),d0 ; Prepare to unload
beq.s 29$
move.l d0,a5
moveq #gmod_EndMusic,d0
gmodmaycall d0
clr.l gmodptr(a4)
29$
clr.l songcatalog(a4) ; Erase all pointers to memory we are about to free
clr.l infolist(a4)
bsr _infowinattach ; Clear out the info window
lea modremember(a4),a0 ; Free any miscellaneous memory we allocated for the module
moveq #1,d0
move.l _IntuitionBase(a4),a6
jl FreeRemember
bsr closeloadhan ; Close any handle we were trying to load with
lea modspec(a4),a0 ; Free the module memory
bsr unloadfile
lea suppspec(a4),a0 ; Free any secondary data file
bsr unloadfile
moveq #1,d0 ; Default to one song
move.l d0,numsongs(a4)
clr.l cursong(a4)
clr.b modtype(a4) ; Default no special module type
clr.l authorname(a4) ; Control-panel text fields
clr.l modtypename(a4)
clr.l modname(a4)
clr.l ntauthor(a4)
clr.b noaudalloc(a4)
moveq #-1,d0
move.l d0,_songlen(a4)
move.w d0,_fadevol(a4)
bset #SB_WINDOWUP,_sysflags(a4)
bsr freemodfile ; Free the modfile buffer
move.l stackpt(a4),sp ; Return to caller
move.l d3,d0
movem.l (sp)+,d2-d7/a2-a6
rts
*** moveabs - Move the module to an absolute position in memory (reload if necessary)
* a0 = Position to move module to
* Returns: a0 = New module position
moveabs:
apush4
move.l modsize(a4),d0 ; Allocate the required memory
move.l d0,d2
move.l a0,a1
move.l a0,a2
move.l _SysBase(a4),a6
jl AllocAbs
tst.l d0
bz.b 1$
move.l modmem(a4),a0 ; Copy the module to the final location
move.l a2,a1
move.l d2,d0
jl CopyMem
move.l modmem(a4),a1 ; Free the old memory block
move.l modalloc(a4),d0
jl FreeMem
move.l a2,modmem(a4) ; Point to the new block
move.l d2,modalloc(a4)
add.l d2,a2
move.l a2,modend(a4)
bra 9$
1$ move.l modmem(a4),a1 ; First free the old block
move.l modalloc(a4),d0
jl FreeMem
clr.l modmem(a4)
move.l a2,a1 ; Now see if we can allocate the memory
move.l d2,d0
jl AllocAbs
tst.l d0
bz dienoabs
move.l a2,modmem(a4) ; Point to the new memory block
move.l d2,modalloc(a4)
add.l d2,a2
move.l a2,modend(a4)
lea modspec(a4),a1 ; Re-load the file at the required address
move.l modfile(a4),a0
bsr loadfile
beq dienofile
9$ move.l modmem(a4),a0
apop4
rts
*** hookfunc - Hook function called by modules
* a1 = Parameter packet
hookfunc:
move.l (a1)+,d0
cmpi.l #GMODHF_SEQUENCE,d0
beq.s \sequence
cmpi.l #GMODHF_REPEAT,d0
beq intrepeat
moveq #0,d0
rts
\sequence:
move.l (a1)+,d0
move.l (a1)+,d1
bra intsongpos
*** getfreqmodspeed - Get the default module speed and set the MODSPEED flag
getfreqmodspeed:
moveq #50,d0
bset #MTB_MODSPEED,modtype(a4)
rts
*** parseauthor - Find the author name in a string which potentially contains one
* a0 = Pointer to string
* Returns:
* a0 = Pointer to beginning of author name or NULL if none found
* a1 = Pointer to first character relating to the author name (<= a0)
parseauthor:
move.l a0,a1
bra.s 11$ ; Search for 'by' keyword
1$ move.l a0,a1
move.b (a0)+,d0
bz.s 9$
cmp.b #$a9,d0 ; Check for "©"
beq 99$
bsr isalpha
bnz.s 1$
11$ move.b (a0),d0
bsr toupper
cmp.b #'C',d0 ; Check for "(C)"
bne.s 12$
cmp.b #'(',-1(a0)
bne.s 12$
cmp.b #')',1(a0)
beq.s 92$
12$ cmp.b #'B',d0 ; Check for "BY"
bne.s 1$
move.b 1(a0),d0
bsr toupper
cmp.b #'Y',d0
bne.s 1$
move.b 2(a0),d0
bsr isalpha
bnz.s 1$
92$ addq.l #2,a0
99$ rts
9$ sub.l a0,a0 ; Return null for both strings
move.w a0,a1
rts
*** prepstring - Prepare author/song name string for display - chop puctuation, etc.
* a0 = String to prepare
* Returns:
* a0 = New pointer to string
prepstring:
move.l a2,-(sp)
bsr stripblanks ; Strip leading and trailing blanks
bz.s 9$
move.l a0,a1 ; Hunt down and eliminate EdPlayer junk
1$ move.b (a1)+,d0
bz.s 19$
cmp.b #'`',d0
bne.s 1$
lea -1(a1),a2
move.l a2,-(sp)
cmp.b #'`',(a1)+
bne.s 13$
subq.l #1,a1
addq.l #1,(sp)
13$ move.b (a1)+,(a2)+
bne.s 13$
move.l (sp)+,a1
bra.s 1$
19$
subq.l #1,a1 ; Eliminate some punctuation at the end
21$ cmp.l a0,a1
beq.s \empty
cmp.b #'.',-(a1)
beq.s 25$
cmp.b #',',(a1)
bne.s 29$
25$ clr.b (a1)
bra.s 21$
29$
9$ move.l (sp)+,a2
rts
\empty:
sub.l a0,a0
bra.s 9$
*** ntgetsongname - Get song name (and possibly author) from a NoiseTracker module
ntgetsongname:
move.l modmem(a4),a0 ; See if the author's name is in the title
bsr parseauthor
move.l a0,d0
bz.s 9$
clr.b (a1) ; Separate it from the title
bsr prepstring
move.l a0,ntauthor(a4)
9$ move.l modmem(a4),a0 ; Return the title
bsr prepstring
move.l a0,d0
rts
*** ntgetsongauthor - Get author name from a NoiseTracker module
ntgetsongauthor:
movem.l a2-a3,-(sp)
move.l ntauthor(a4),d0 ; See if we already found the author
bnz 9$
bsr addinfopad ; Display the instrument list verbatim
move.l modmem(a4),a0
lea 20(a0),a2
lea 31*30(a2),a3
\info
tst.b (a2)
bz.b \noinfo
move.l a2,a0
bsr addinfo
\noinfo
lea 30(a2),a2
cmp.l a3,a2
bne.b \info
move.l modmem(a4),a0 ; Find an entry with author keywords
lea 20(a0),a2
lea 31*30(a2),a3
1$ move.l a2,a0
bsr parseauthor
move.l a0,d0
bnz.s 2$
lea 30(a2),a2
cmp.l a3,a2
bne.s 1$
move.l modmem(a4),a0 ; Find an IntuiTracker scroll line
lea 20(a0),a0
lea 31*30(a0),a3
3$ cmp.b #'#',(a0)+
beq.s 4$
lea 30-1(a0),a0
cmp.l a3,a0
bne.s 3$
moveq #0,d0 ; Nothing found at all
bra.s 9$
2$ bsr prepstring ; Strip leading and trailing blanks
move.l a0,d0
bnz.s 9$
lea 30(a2),a0 ; Nothing on that line - use the next line
cmp.b #'#',(a0) ; Skip leading IntuiTracker junk
bne.s 4$
addq.l #1,a0
cmp.b #'#',(a0) ; If there are *two*, it's probably decoration
bne.s 4$
subq.l #1,a0
4$ bsr prepstring
move.l a0,d0
9$ movem.l (sp)+,a2-a3
rts
*** gmodstart - Code for general modules (GMOD's)
gmodstart:
move.l gmod_LoadAddress(a0),d1 ; Move the module if necessary
beq.s 19$
move.l d1,a0
bsr moveabs
19$ move.l a0,d0
move.l gmod_Maker(a0),gmodnametype(a4) ; Find the module's maker ID
rts
*** amodstart - Code for Absolute Executable modules (AMOD's)
amodloc equ $10
amodstart:
move.l amodloc(a0),a0 ; Move to the required position
bsr moveabs
; Fall through and execute like an XMOD...
*** xmodstart - Code for Executable modules (XMOD's)
xminitentry equ $4
xmvbentry equ $8
xmendentry equ $c
xmodstart:
plstartret 9$
cnop 0,4
dc.l gmod_Hook
9$ gmodnop
gmodbra 2$ ; StartMusic
gmodbra 3$ ; StopMusic
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodbra getfreqmodspeed ; GetFrequency
jmp xmvbentry(a1) ; TimerTick
lea xmodname(a4),a0 ; GetMakerName
move.l a0,d0
rts
2$ move.l modmem(a4),a0
jmp xminitentry(a0)
3$ move.l modmem(a4),a0
jmp xmendentry(a0)
*** ntstart - Code to differentiate between ST/NT/PT and StarTrekker
ntststart:
move.l modfile(a4),a0 ; Check for a StarTrekker ".NT" file
move.l sp,a2
moveq #4,d0
11$ addq.l #1,d0
tst.b (a0)+
bne.s 11$
bclr #0,d0
sub.l d0,sp
move.l sp,a1
move.l modfile(a4),a0
bra.s 13$
12$ move.b d0,(a1)+
13$ move.b (a0)+,d0
bne.s 12$
move.b #'.',(a1)+
move.b #'N',(a1)+
move.b #'T',(a1)+
clr.b (a1)
move.l sp,a0
lea suppspec(a4),a1
bsr loadfile
move.l a2,sp
bnz trekstart ; Go to appropriate startup
bra ntstart
*** genembstart - Code for generic embedded-player modules (such as NoiseTracker packed w/player)
genembstart:
cmpi.w #$4efa,4(a0) ; Make sure the other two JMP's exist
bne diecorrupt
cmpi.w #$4efa,8(a0)
bne diecorrupt
plstartret 9$
cnop 0,4
dc.l gmod_Hook
9$ gmodnop
gmodbra 2$ ; StartMusic
gmodbra 3$ ; StopMusic
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodbra getfreqmodspeed ; GetFrequency
gmodbra 5$ ; TimerTick
lea genembname(a4),a0 ; GetMakerName
move.l a0,d0
rts
2$ movem.l d2-d7/a2-a6,-(sp)
moveq #0,d0
move.l modmem(a4),a5
jsr (a5)
movem.l (sp)+,d2-d7/a2-a6
rts
3$ movem.l d2-d7/a2-a6,-(sp)
move.l modmem(a4),a5
jsr 8(a5)
movem.l (sp)+,d2-d7/a2-a6
rts
5$ movem.l d2-d7/a2-a6,-(sp)
jsr 4(a1)
movem.l (sp)+,d2-d7/a2-a6
rts
*** fxstart - Code for SoundFX modules
fxstart:
movem.l a5/a6,-(sp)
move.l modmem(a4),a5
move.l _SysBase(a4),a6
lea $80(a5),a0 ; Make sure it's a SoundFX module
move.w #($a20-$80)/4-1,d0
moveq #0,d1
1$ add.l (a0)+,d1
dbra d0,1$
cmpi.l #$97adddd8,d1
bne diecorrupt
move.l $20(a5),d0 ; Relocate the player
add.l d0,d0
add.l d0,d0
cmp.l modsize(a4),d0
bge diecorrupt
lea $30(a5,d0.l),a0
cmpi.l #$3ec,-$c(a0)
bne diecorrupt
moveq #49-1,d0
lea $24(a5),a1
move.l a1,d2
12$ move.l (a0)+,d1
add.l d2,0(a1,d1.l)
dbra d0,12$
move.l #$4e714e71,$196(a5) ; NOP out AddICRVector
move.l #$4e714e71,$1a8(a5) ; NOP out call to set CIA stuff
bsr clearcache
movem.l (sp)+,a5/a6
plstartret 9$
2$ movem.l d2-d7/a2-a6,-(sp) ; Jump into player start code
move.l modmem(a4),a0
jsr $13e(a0)
movem.l (sp)+,d2-d7/a2-a6
rts
5$ move.l modmem(a4),a0 ; Calculate interrupt frequency
move.l #715909,d0
divu.w $a64(a0),d0
ext.l d0
rts
cnop 0,4
dc.l gmod_Hook
9$ gmodnop
gmodbra 2$ ; StartMusic
gmodnop
gmodnop
gmodnop
gmodq 1
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodbra 5$ ; GetFrequency
jmp $466(a1) ; TimerTick
lea fxname(a4),a0 ; GetMakerName
move.l a0,d0
rts
*** whitstart - Code for Dave Whittaker modules
dwinitentry equ $00
dwvbentry equ $0e
dwendentry equ $1c
whitstart:
moveq #6-1,d0 ; Make sure it's really a DW module
1$ move.w $0(a0),d1
add.w $4(a0),d1
add.w $8(a0),d1
add.w $c(a0),d1
cmpi.w #$453b,d1
bne diecorrupt
adda.w #$e,a0
dbra d0,1$
plstartret 9$
cnop 0,4
dc.l gmod_Hook
9$ gmodnop
gmodbra 2$ ; StartMusic
gmodbra 3$ ; StopMusic
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodbra getfreqmodspeed ; GetFrequency
jmp dwvbentry(a1) ; TimerTick
lea whitname(a4),a0 ; GetMakerName
move.l a0,d0
rts
2$ move.l modmem(a4),a0
jmp dwinitentry(a0)
3$ move.l modmem(a4),a0
jmp dwendentry(a0)
*** deltastart - Code for Delta Music modules
deltastart:
plstartret 9$
cnop 0,4
dc.l gmod_Hook
9$ gmodnop
gmodbra 2$ ; StartMusic
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodbra getfreqmodspeed ; GetFrequency
gmodbra 4$ ; TimerTick
lea deltaname(a4),a0 ; GetMakerName
move.l a0,d0
rts
2$ push d2-d7/a2-a6
move.l modmem(a4),a0
moveq #1,d0
jsr (a0)
pop d2-d7/a2-a6
rts
4$ push d2-d7/a2-a6
moveq #0,d0
jsr (a1)
pop d2-d7/a2-a6
rts
delta2start: ; Different version (older probably)
plstartret 9$
cnop 0,4
dc.l gmod_Hook
9$ gmodnop
gmodbra 2$ ; StartMusic
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodbra getfreqmodspeed ; GetFrequency
gmodbra 4$ ; TimerTick
lea deltaname(a4),a0 ; GetMakerName
move.l a0,d0
rts
2$ push d2-d7/a2-a6
move.l modmem(a4),a0
jsr (a0)
pop d2-d7/a2-a6
rts
4$ push d2-d7/a2-a6
jsr $17c(a1)
pop d2-d7/a2-a6
rts
*** sid2start - Code for SidMon modules
sid2start:
plstartret 9$
cnop 0,4
dc.l gmod_Hook
9$ gmodnop
gmodbra 2$ ; StartMusic
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodbra getfreqmodspeed ; GetFrequency
jmp $16a(a1) ; TimerTick
lea sidname(a4),a0 ; GetMakerName
move.l a0,d0
rts
2$ move.l modmem(a4),a0 ; Put an RTS after the interrupt routine
move.w #$4e75,$25c(a0)
bsr clearcache
move.l modmem(a4),a0 ; Call the initialization routine
push d2-d7/a2-a6
jsr $2c(a0)
pop d2-d7/a2-a6
rts
*** markiistart - Code for Mark II modules
markiistart:
cmpi.l #$41fa035e,4(a0) ; Check a little more...
bne diecorrupt
plstartret 9$
cnop 0,4
dc.l gmod_Hook
9$ gmodnop
gmodbra 2$ ; StartMusic
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodbra getfreqmodspeed ; GetFrequency
gmodbra 3$ ; TimerTick
lea markiiname(a4),a0 ; GetMakerName
move.l a0,d0
rts
2$ move.l modmem(a4),a0
moveq #-1,d0
push d2-d7/a2-a6
jsr (a0)
pop d2-d7/a2-a6
rts
3$ moveq #0,d0
push d2-d7/a2-a6
jsr (a1)
pop d2-d7/a2-a6
rts
*** ftmstart - Code for Face The Music modules
ftm_init equ 4
ftm_end equ 8
ftm_load equ 12 ; d0 = filename, returns nonzero = success
ftm_unload equ 16
ftm_play equ 20
ftm_stop equ 24
ftm_set1 equ 28
ftm_set2 equ 32
ftm_get1 equ 36
ftm_get2 equ 40
ftm_waitdone equ 44
ftm_get3 equ 48
ftm_ccall equ 52
ftmstart:
apush4
st.b noaudalloc(a4)
lea modspec(a4),a0 ; We can't use the already-loaded module!
bsr unloadfile
move.l _DOSBase(a4),a6 ; Load PlayFTM
lea playftmname(a4),a0
move.l a0,d1
jl LoadSeg
move.l d0,ftmseg(a4)
bz.b \noplayer
lsl.l #2,d0 ; Find jump table (a5, ftmtab)
addq.l #4,d0
move.l d0,ftmtab(a4)
move.l d0,a5
jsr ftm_init(a5) ; Initialize PlayFTM
move.l modfile(a4),d0 ; Re-load the module through PlayFTM
jsr ftm_load(a5)
tst.l d0
bz \corrupt
apop4
plstartret 9$
\noplayer
lea ftmnoplayer(a4),a0
bra diemes
\corrupt
lea corrcomp(a4),a0
bra diemes
\start
moveq #0,d0 ; Start playing
moveq #0,d1
moveq #0,d2
moveq #0,d3
move.l ftmtab(a4),a0
jmp ftm_play(a0)
\stop
move.l ftmtab(a4),a0
jmp ftm_stop(a0)
\end
move.l ftmtab(a4),a0
jsr ftm_unload(a0)
jmp ftm_end(a0)
cnop 0,4
dc.l gmod_Hook
9$ gmodnop
gmodbra \start
gmodbra \stop
gmodbra \end
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
gmodnop
lea ftmname(a4),a0 ; GetMakerName
move.l a0,d0
rts
*** unloadftm - FIXME - KLUDGE - unload the FTM player (called by main.c)
xdef _unloadftm
_unloadftm
apush4
move.l _DOSBase(a4),a6
move.l ftmseg(a4),d1
bz.b 1$
jl UnLoadSeg
1$ apop4
rts
*** setnormtint - Set the timer interrupt using the current module's default speed
* a0 = Pointer to interrupt routine to be called on interrupts
* a1 = Data that should appear in register a1 on interrupt calls
setnormtint:
moveq #50,d0
; fall through...
*** settint - Set the timer interrupt
* a0 = Pointer to interrupt routine to be called on interrupts
* a1 = Data that should appear in register a1 on interrupt calls
* d0 = Number of ticks per second (frequency) to call interrupt routine
settint:
movem.l a2/a3,-(sp)
move.l a0,sint+IS_CODE(a4) ; Initialize the timer interrupt
move.l a1,sint+IS_DATA(a4)
move.b #NT_INTERRUPT,d1
move.b d1,sint+LN_TYPE(a4)
move.b d1,tint+LN_TYPE(a4)
lea sint(a4),a0
move.l a0,tint+IS_DATA(a4)
move.l _SysBase(a4),a0
lea _LVOCause(a0),a0
move.l a0,tint+IS_CODE(a4)
move.w d0,-(sp)
move.l _BattMemBase(a4),a6
lea tint(a4),a1 ; First try to get timer B
moveq #CIAICRB_TB,d0
move.b d0,timernum(a4)
move.l #CIAB+ciatblo,a2
move.l a2,timerlo(a4)
move.l #CIAB+ciacrb,a3
jsr _LVOAddICRVector(a6)
tst.l d0
beq.s 1$
lea tint(a4),a1 ; Already taken - try timer A
moveq #CIAICRB_TA,d0
move.b d0,timernum(a4)
move.l #CIAB+ciatalo,a2
move.l a2,timerlo(a4)
move.l #CIAB+ciacra,a3
jsr _LVOAddICRVector(a6)
tst.l d0
bne dienotimer
1$
and.b #%11000000,(a3) ; Set the control register
tst.b timernum(a4)
beq.s 3$
bclr #6,(a3)
3$
move.w (sp)+,d0 ; Set the timer frequency
bsr changetint
bset #0,(a3) ; Start the timer
movem.l (sp)+,a2-a3
move.b timernum(a4),d1 ; Enable the timer interrupt
moveq #-$80,d0
bset d1,d0
jmp _LVOAbleICR(a6)
*** changetint - Change the timer interrupt speed (callable from an interrupt)
* d0 = New frequency (Hz)
* All registers saved
changetint:
tst.l tint+IS_CODE
bz.b 9$
push d0-d1/a0-a1
lea _LinkerDB,a1
move.w d0,tintfreq(a1) ; Store the new frequency
tst.l tint+IS_CODE(a1)
bz.b 9$
mulu.w mastfreq(a1),d0
lsr.l #8,d0
bz.b \zero
move.l clockfreq(a1),d1 ; Poke the new timer value
divu.w d0,d1
move.l timerlo(a1),a0
move.b d1,(a0)
lsr.w #8,d1
move.b d1,$100(a0)
\zero
pop d0-d1/a0-a1
9$ rts
*** changetintcia - Change the timer interrupt speed (callable from an interrupt)
* d0 = CIA speed value
* All registers saved
changetintcia:
push d0-d1/a0-a1
lea _LinkerDB,a1
tst.l tint+IS_CODE(a1)
bz.b 9$
move.l timerlo(a1),a0 ; Poke the new timer value
moveq #0,d1
move.w d0,d1
lsl.l #8,d1
divu.w mastfreq(a1),d1
move.b d1,(a0)
lsr.w #8,d1
move.b d1,$100(a0)
move.l clockfreq(a1),d1 ; Find the new frequency to display
divu.w d0,d1
move.w d1,tintfreq(a1)
9$ pop d0-d1/a0-a1
rts
*** stoptint - Stop the timer interrupt if it was installed
stoptint:
tst.l tint+IS_CODE(a4) ; Make sure it was installed
beq.s 9$
moveq #0,d0 ; Remove the interrupt handler
move.b timernum(a4),d0
lea tint(a4),a1
move.l _BattMemBase(a4),a6
jsr _LVORemICRVector(a6)
clr.l tint+IS_CODE(a4)
9$ rts
*** playervolume - Update the master volume for the currently playing module
playervolume
_playervolume
apush4
tst.b _playing(a4) ; Make sure we're playing something
bz \out
move.w mbalance(a4),d2 ; Find the new overall volume (d0)
move.w balance(a4),d3
move.w mvolume(a4),d1
move.w volume(a4),d0
bpl.b \hasvol
moveq #100,d0
moveq #100,d1
moveq #0,d2
moveq #0,d3
\hasvol
mulu.w d1,d0
lsl.l #8,d0
divu.w #10000,d0
move.w d0,d1
tst.w d2 ; Adjust for master balance
bz.b \mgot
bmi.b \mmoreleft
\mmoreright
neg.w d2
add.w #50,d2
mulu.w d2,d0
divu.w #50,d0
bra.b \mgot
\mmoreleft
add.w #50,d2
mulu.w d2,d1
divu.w #50,d1
\mgot
tst.w d3 ; Adjust for module balance
bz.b \got
bmi.b \moreleft
\moreright
neg.w d3
add.w #50,d3
mulu.w d3,d0
divu.w #50,d0
bra.b \got
\moreleft
add.w #50,d3
mulu.w d3,d1
divu.w #50,d1
\got
move.w _fadevol(a4),d2 ; Adjust for fading
bmi.b \nofade
mulu.w d2,d0
lsr.w #7,d0
mulu.w d2,d1
lsr.w #7,d1
\nofade
tst.b portablemodule(a4) ; Tell the module about the new volume
bnz \portavol
move.l gmodptr(a4),a5
moveq #gmod_SetVolume,d2
gmodmaycall d2
bra \out
\portavol
bsr NoteSysMasterVolBal
\out
apop4
rts
*** playerspeed - Update the master speed
playerspeed
_playerspeed
push a6
move.w speed(a4),d0 ; Calculate the new relative speed
bnz.b \ok
moveq #50,d0
\ok
move.w mspeed(a4),d1
mulu.w d1,d0
lsl.l #8,d0
divu.w #50*50,d0
move.w d0,mastfreq(a4)
move.l _SysBase(a4),a6 ; Set the new speed
jl Disable
move.w tintfreq(a4),d0
bsr changetint
jl Enable
pop a6
rts
*** jumpforward - Jump one sequence forward in the module
jumpforward
move.l gmodptr(a4),d0
bz \out
push d5/a5
move.l d0,a5
moveq #0,d0
move.l songpos(a4),d1
addq.l #1,d1
moveq #gmod_Jump,d5
gmodmaycall d5
pop d5/a5
\out
rts
*** jumpback - Jump one sequence backward in the module
jumpback
move.l gmodptr(a4),d0
bz \out
push d5/a5
move.l d0,a5
moveq #0,d0
move.l songpos(a4),d1
bz.b \out1
subq.l #1,d1
moveq #gmod_Jump,d5
gmodmaycall d5
\out1
pop d5/a5
\out
rts
*** iscantcont - See if an error is a 'can't continue' error
* a0 = Pointer to message in question
* Returns: d0 = nonzero if a0 is a can't continue error
_iscantcont
move.l 4(sp),a0
iscantcont
moveq #0,d0
lea nocontinue(a4),a1
cmp.l a1,a0
seq d0
rts
*** dmawait - DMA delay wait
dmawait:
movem.l d0-d1,-(sp)
moveq #7-1,d1
1$ move.b $dff006,d0
2$ cmp.b $dff006,d0
beq.s 2$
dbf d1,1$
movem.l (sp)+,d0-d1
rts
*** clearcache - Clear the code cache
clearcache:
move.l a6,-(sp)
move.l 4,a6
cmpi.w #36,LIB_VERSION(a6)
bcs.s 13$
jsr _LVOCacheClearU(a6)
13$ move.l (sp)+,a6
rts
data __MERGED
_numsongs:
numsongs dc.l 1 ; Number of songs in this module
_cursong:
cursong dc.l 0 ; Current song number
playerhook dc.l 0,0,hookfunc ; Hook to send to modules
mastfreq dc.w $100 ; Master relative frequency ($100=normal)
_fadevol dc.w -1 ; Current fade volume
even
gmodname dc.b "GMOD ("
gmodnametype dc.l 0
dc.b ")",0
nocmem dc.b "Not enough chip memory",0
nomemtext dc.b "Not enough memory",0
unkmod dc.b "Unknown module type",0
cormod dc.b "Corrupt module",0
errplay dc.b "Error playing module",0
nofile dc.b "Can't load module",0
noabs dc.b "Required memory occupied",0
notimer dc.b "No CIAB timers available",0
dontcompress dc.b "Please decompress this module",0
genembname dc.b "Generic embedded-player",0
xmodname dc.b "XMOD/AMOD",0
whitname dc.b "Dave Whittaker",0
deltaname dc.b "Delta Music",0
sidname dc.b "SidMon",0
markiiname dc.b "Mark II",0
fxname dc.b "SoundFX",0
unknowntxt dc.b "Unknown",0
notloaded dc.b "No module loaded",0
nocontinue dc.b "Module cannot continue",0
audiointname dc.b "MultiPlayer audio interrupt",0
playftmname dc.b "PlayFTM:",0
ftmnoplayer dc.b "Can't load Face The Music player",0
corrcomp dc.b "Corrupt (or compressed) FTM module",0
ftmname dc.b "Face The Music"
nulllab dc.b 0
bss __MERGED
stackpt ds.l 1 ; Stack pointer when we die while loading
loadhan ds.l 1 ; Handle currently being loaded
gmodptr ds.l 1 ; Pointer to real or fake GMOD structure
modspec: ; Currently loaded module
modmem ds.l 1
modsize ds.l 1
modend ds.l 1
modalloc ds.l 1
suppspec: ; Supplemental data file (Startrekker, TFMX)
suppmem ds.l 1
suppsize ds.l 1
suppend ds.l 1
suppalloc ds.l 1
modfile ds.l 1 ; Filename of module
_modname:
modname ds.l 1 ; Displayed name of module
modfilesize ds.l 1 ; Allocated size of modfile
_authorname:
authorname ds.l 1 ; Name of song's author
_modtypename:
modtypename ds.l 1 ; Type of module
_songcatalog:
songcatalog ds.l 1 ; Catalog of song names for current module
_infolist:
infolist ds.l 1 ; List of information lines for Info window
cursongprefs ds.l 1 ; Pointer into songprefs for current song
clockfreq ds.l 1 ; CIA clock frequency
_songtime ds.l 1 ; Number of VBlanks this song has played
_songendtime ds.l 1 ; When this song should end
_songpos
songpos ds.l 1 ; Current sequence number
_songlen
songlen ds.l 1 ; Total number of sequences in song
ntauthor ds.l 1 ; Author name from NoiseTracker modules
ftmseg ds.l 1 ; SegList of PlayFTM
ftmtab ds.l 1 ; FTM jump table
timerlo ds.l 1 ; Address of CIA timer low counter register
audints ds.b IS_SIZE*4 ; Audio channel interrupts
audisave ds.l 4 ; Previous interrupt vectors
modremember ds.l 1 ; Remember list attached to this module
tint ds.b IS_SIZE
sint ds.b IS_SIZE
tintfreq ds.w 1 ; Timer interrupt frequency (un-user-adjusted)
_fadeinc ds.w 1 ; +1 or -1 - which way to fade
xpkerrmsg ds.b 80 ; Buffer for XPK error messages
_playing ds.b 1 ; Module is currently playing
noaudalloc ds.b 1 ; Don't allocate the audio hardware
timernum ds.b 1 ; Which CIAB timer we got
_modtype
modtype ds.b 1 ; Module type flags
portablemodule ds.b 1 ; Nonzero if we're playing a portable module
end